---
title: "错误处理与类型安全"
description: "学习 Rust 的错误处理机制，包括 Result、Option 类型和错误传播，对比 JavaScript 的异常处理"
---

# 错误处理与类型安全

## 📖 学习目标

理解 Rust 的错误处理哲学，学会使用 `Result` 和 `Option` 类型，掌握错误传播模式，对比 JavaScript 的异常处理机制。

---

## 🎯 错误处理哲学对比

### JavaScript 的异常处理

JavaScript 使用 try-catch 机制处理异常：

<UniversalEditor title="JavaScript 异常处理" compare={true}>
```javascript !! js
// JavaScript 异常处理
function divide(a, b) {
    if (b === 0) {
        throw new Error("除数不能为零");
    }
    return a / b;
}

function processData(data) {
    if (!data) {
        throw new Error("数据不能为空");
    }
    return data.toUpperCase();
}

// 使用 try-catch 处理异常
try {
    const result1 = divide(10, 2);
    console.log("除法结果:", result1);
    
    const result2 = divide(10, 0); // 抛出异常
    console.log("这个不会执行");
} catch (error) {
    console.log("捕获到异常:", error.message);
}

// 异步异常处理
async function fetchData(url) {
    try {
        const response = await fetch(url);
        if (!response.ok) {
            throw new Error(`HTTP 错误: ${response.status}`);
        }
        return await response.json();
    } catch (error) {
        console.log("网络请求失败:", error.message);
        return null;
    }
}

// Promise 异常处理
function asyncOperation() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const random = Math.random();
            if (random > 0.5) {
                resolve("操作成功");
            } else {
                reject(new Error("操作失败"));
            }
        }, 1000);
    });
}

asyncOperation()
    .then(result => console.log("成功:", result))
    .catch(error => console.log("失败:", error.message));

// 可选链和空值合并
function getUserName(user) {
    // 传统方式
    if (user && user.profile && user.profile.name) {
        return user.profile.name;
    }
    return "未知用户";
    
    // 现代方式
    // return user?.profile?.name ?? "未知用户";
}

// 函数可能返回 null 或 undefined
function findUser(id) {
    const users = [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" }
    ];
    
    const user = users.find(u => u.id === id);
    return user || null; // 可能返回 null
}

const user = findUser(3);
if (user) {
    console.log("找到用户:", user.name);
} else {
    console.log("用户不存在");
}
```
</UniversalEditor>

### Rust 的错误处理

Rust 使用类型系统处理错误，没有异常机制：

<UniversalEditor title="Rust 错误处理" compare={true}>
```rust !! rs
// Rust 错误处理 - 使用 Result 类型
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("除数不能为零".to_string())
    } else {
        Ok(a / b)
    }
}

fn process_data(data: &str) -> Result<String, String> {
    if data.is_empty() {
        return Err("数据不能为空".to_string());
    }
    Ok(data.to_uppercase())
}

// 使用 match 处理 Result
fn handle_division() {
    match divide(10.0, 2.0) {
        Ok(result) => println!("除法结果: {}", result),
        Err(error) => println!("错误: {}", error),
    }
    
    match divide(10.0, 0.0) {
        Ok(result) => println!("结果: {}", result),
        Err(error) => println!("错误: {}", error),
    }
}

// 使用 if let 简化处理
fn handle_division_simple() {
    if let Ok(result) = divide(10.0, 2.0) {
        println!("除法结果: {}", result);
    }
    
    if let Err(error) = divide(10.0, 0.0) {
        println!("错误: {}", error);
    }
}

// 使用 unwrap 和 expect（不推荐在生产代码中使用）
fn handle_division_unwrap() {
    let result = divide(10.0, 2.0).unwrap(); // 成功时返回值，失败时 panic
    println!("结果: {}", result);
    
    let result = divide(10.0, 0.0).expect("除法失败"); // 失败时 panic 并显示消息
    println!("结果: {}", result);
}

// 使用 ? 操作符传播错误
fn process_operations() -> Result<(), String> {
    let result1 = divide(10.0, 2.0)?; // 如果失败，立即返回错误
    println!("第一个结果: {}", result1);
    
    let result2 = process_data("hello")?;
    println!("第二个结果: {}", result2);
    
    Ok(()) // 成功时返回 ()
}

fn main() {
    handle_division();
    handle_division_simple();
    
    // 处理可能 panic 的代码
    match std::panic::catch_unwind(|| {
        handle_division_unwrap();
    }) {
        Ok(_) => println!("操作成功"),
        Err(_) => println!("操作失败，发生 panic"),
    }
    
    // 处理返回 Result 的函数
    if let Err(error) = process_operations() {
        println!("处理操作时出错: {}", error);
    }
}
```
</UniversalEditor>

### 错误处理差异

1. **异常 vs 类型**: JavaScript 使用异常，Rust 使用类型系统
2. **运行时 vs 编译时**: JavaScript 运行时检查，Rust 编译时检查
3. **可恢复 vs 不可恢复**: Rust 区分可恢复错误（Result）和不可恢复错误（panic）
4. **显式 vs 隐式**: Rust 强制显式处理错误，JavaScript 可以忽略异常

---

## 📦 Option 类型

### 处理可能为空的值

<UniversalEditor title="Option 类型" compare={true}>
```rust !! rs
// Option 类型 - 处理可能为空的值
fn find_user(id: u32) -> Option<User> {
    let users = vec![
        User { id: 1, name: String::from("Alice") },
        User { id: 2, name: String::from("Bob") },
    ];
    
    users.into_iter().find(|user| user.id == id)
}

fn get_user_name(user: &User) -> &str {
    &user.name
}

// 使用 match 处理 Option
fn handle_user_match(id: u32) {
    match find_user(id) {
        Some(user) => println!("找到用户: {}", get_user_name(&user)),
        None => println!("用户不存在"),
    }
}

// 使用 if let 简化处理
fn handle_user_if_let(id: u32) {
    if let Some(user) = find_user(id) {
        println!("找到用户: {}", get_user_name(&user));
    } else {
        println!("用户不存在");
    }
}

// 使用 map 和 and_then 进行链式操作
fn handle_user_chain(id: u32) {
    let user_name = find_user(id)
        .map(|user| get_user_name(&user).to_string())
        .unwrap_or_else(|| "未知用户".to_string());
    
    println!("用户名: {}", user_name);
}

// 使用 unwrap 和 expect（谨慎使用）
fn handle_user_unwrap(id: u32) {
    let user = find_user(id).unwrap(); // 如果为 None 则 panic
    println!("用户: {}", get_user_name(&user));
    
    let user = find_user(id).expect("用户应该存在"); // 自定义 panic 消息
    println!("用户: {}", get_user_name(&user));
}

// Option 的实用方法
fn option_methods() {
    let value: Option<i32> = Some(42);
    let none_value: Option<i32> = None;
    
    // unwrap_or - 提供默认值
    println!("值: {}", value.unwrap_or(0)); // 42
    println!("值: {}", none_value.unwrap_or(0)); // 0
    
    // unwrap_or_else - 使用闭包提供默认值
    println!("值: {}", none_value.unwrap_or_else(|| {
        println!("计算默认值");
        100
    }));
    
    // map - 转换值
    let doubled = value.map(|x| x * 2);
    println!("加倍: {:?}", doubled); // Some(84)
    
    // and_then - 链式操作
    let result = value.and_then(|x| {
        if x > 40 {
            Some(x.to_string())
        } else {
            None
        }
    });
    println!("结果: {:?}", result); // Some("42")
    
    // filter - 过滤值
    let filtered = value.filter(|&x| x > 50);
    println!("过滤后: {:?}", filtered); // None
}

struct User {
    id: u32,
    name: String,
}

fn main() {
    handle_user_match(1);
    handle_user_match(3);
    
    handle_user_if_let(1);
    handle_user_if_let(3);
    
    handle_user_chain(1);
    handle_user_chain(3);
    
    option_methods();
}
```
</UniversalEditor>

---

## 🔄 Result 类型

### 处理可能失败的操作

<UniversalEditor title="Result 类型" compare={true}>
```rust !! rs
// 自定义错误类型
#[derive(Debug)]
enum AppError {
    DivisionByZero,
    InvalidInput(String),
    NetworkError(String),
    DatabaseError(String),
}

impl std::fmt::Display for AppError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            AppError::DivisionByZero => write!(f, "除数不能为零"),
            AppError::InvalidInput(msg) => write!(f, "无效输入: {}", msg),
            AppError::NetworkError(msg) => write!(f, "网络错误: {}", msg),
            AppError::DatabaseError(msg) => write!(f, "数据库错误: {}", msg),
        }
    }
}

// 实现 std::error::Error trait
impl std::error::Error for AppError {}

// 使用自定义错误类型
fn divide_with_custom_error(a: f64, b: f64) -> Result<f64, AppError> {
    if b == 0.0 {
        Err(AppError::DivisionByZero)
    } else {
        Ok(a / b)
    }
}

fn validate_input(input: &str) -> Result<String, AppError> {
    if input.is_empty() {
        return Err(AppError::InvalidInput("输入不能为空".to_string()));
    }
    
    if input.len() < 3 {
        return Err(AppError::InvalidInput("输入长度至少为3".to_string()));
    }
    
    Ok(input.to_string())
}

// 模拟网络请求
fn fetch_data(url: &str) -> Result<String, AppError> {
    if url.is_empty() {
        return Err(AppError::NetworkError("URL 不能为空".to_string()));
    }
    
    if url.contains("error") {
        return Err(AppError::NetworkError("模拟网络错误".to_string()));
    }
    
    Ok(format!("从 {} 获取的数据", url))
}

// 错误传播
fn process_data_chain(input: &str, url: &str) -> Result<String, AppError> {
    // 使用 ? 操作符传播错误
    let validated_input = validate_input(input)?;
    let data = fetch_data(url)?;
    
    Ok(format!("处理后的数据: {} + {}", validated_input, data))
}

// 使用 map 和 map_err 转换错误
fn process_with_map(input: &str) -> Result<String, AppError> {
    validate_input(input)
        .map(|s| s.to_uppercase())
        .map_err(|e| AppError::InvalidInput(format!("验证失败: {}", e)))
}

// 使用 and_then 进行链式操作
fn process_with_and_then(input: &str) -> Result<String, AppError> {
    validate_input(input)
        .and_then(|s| {
            if s.len() > 10 {
                Ok(s)
            } else {
                Err(AppError::InvalidInput("输入太短".to_string()))
            }
        })
        .map(|s| format!("处理结果: {}", s))
}

// 组合多个 Result
fn combine_results() -> Result<(), AppError> {
    let result1 = divide_with_custom_error(10.0, 2.0)?;
    let result2 = validate_input("hello")?;
    let result3 = fetch_data("https://example.com")?;
    
    println!("所有操作成功: {}, {}, {}", result1, result2, result3);
    Ok(())
}

fn main() {
    // 处理自定义错误
    match divide_with_custom_error(10.0, 0.0) {
        Ok(result) => println!("结果: {}", result),
        Err(error) => println!("错误: {}", error),
    }
    
    // 使用 ? 操作符
    if let Err(error) = process_data_chain("hello", "https://example.com") {
        println!("处理失败: {}", error);
    }
    
    // 使用 map 和 and_then
    match process_with_map("hi") {
        Ok(result) => println!("成功: {}", result),
        Err(error) => println!("失败: {}", error),
    }
    
    match process_with_and_then("very long input string") {
        Ok(result) => println!("成功: {}", result),
        Err(error) => println!("失败: {}", error),
    }
    
    // 组合多个操作
    if let Err(error) = combine_results() {
        println!("组合操作失败: {}", error);
    }
}
```
</UniversalEditor>

---

## 🎯 错误处理最佳实践

### 错误处理模式对比

<UniversalEditor title="错误处理模式" compare={true}>
```javascript !! js
// JavaScript 错误处理模式
class ValidationError extends Error {
    constructor(message) {
        super(message);
        this.name = 'ValidationError';
    }
}

class NetworkError extends Error {
    constructor(message) {
        super(message);
        this.name = 'NetworkError';
    }
}

// 函数可能抛出异常
function processUserData(userData) {
    if (!userData.name) {
        throw new ValidationError('用户名不能为空');
    }
    
    if (userData.age < 0) {
        throw new ValidationError('年龄不能为负数');
    }
    
    return {
        ...userData,
        processed: true
    };
}

// 异步函数错误处理
async function fetchUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) {
            throw new NetworkError(`HTTP ${response.status}`);
        }
        return await response.json();
    } catch (error) {
        if (error instanceof NetworkError) {
            console.log('网络错误:', error.message);
        } else {
            console.log('未知错误:', error.message);
        }
        throw error; // 重新抛出
    }
}

// 使用 try-catch 处理
async function handleUser(userId, userData) {
    try {
        const processedData = processUserData(userData);
        const fetchedData = await fetchUserData(userId);
        
        return {
            ...processedData,
            ...fetchedData
        };
    } catch (error) {
        if (error instanceof ValidationError) {
            console.log('验证错误:', error.message);
            return null;
        } else if (error instanceof NetworkError) {
            console.log('网络错误:', error.message);
            return null;
        } else {
            console.log('未知错误:', error.message);
            throw error;
        }
    }
}

// 使用 Promise 链
function processWithPromise(userData) {
    return Promise.resolve(userData)
        .then(data => {
            if (!data.name) {
                throw new ValidationError('用户名不能为空');
            }
            return data;
        })
        .then(data => {
            return {
                ...data,
                processed: true
            };
        })
        .catch(error => {
            if (error instanceof ValidationError) {
                console.log('验证失败:', error.message);
                return null;
            }
            throw error;
        });
}
```

```rust !! rs
// Rust 错误处理模式
use std::error::Error;
use std::fmt;

// 自定义错误类型
#[derive(Debug)]
enum AppError {
    ValidationError(String),
    NetworkError(String),
    DatabaseError(String),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AppError::ValidationError(msg) => write!(f, "验证错误: {}", msg),
            AppError::NetworkError(msg) => write!(f, "网络错误: {}", msg),
            AppError::DatabaseError(msg) => write!(f, "数据库错误: {}", msg),
        }
    }
}

impl Error for AppError {}

// 函数返回 Result
fn process_user_data(user_data: UserData) -> Result<UserData, AppError> {
    if user_data.name.is_empty() {
        return Err(AppError::ValidationError("用户名不能为空".to_string()));
    }
    
    if user_data.age < 0 {
        return Err(AppError::ValidationError("年龄不能为负数".to_string()));
    }
    
    Ok(UserData {
        processed: true,
        ..user_data
    })
}

// 异步函数错误处理
async fn fetch_user_data(user_id: u32) -> Result<UserData, AppError> {
    // 模拟网络请求
    if user_id == 0 {
        return Err(AppError::NetworkError("无效的用户ID".to_string()));
    }
    
    // 模拟成功响应
    Ok(UserData {
        id: user_id,
        name: format!("用户{}", user_id),
        age: 25,
        processed: false,
    })
}

// 使用 ? 操作符处理错误
async fn handle_user(user_id: u32, user_data: UserData) -> Result<UserData, AppError> {
    let processed_data = process_user_data(user_data)?;
    let fetched_data = fetch_user_data(user_id).await?;
    
    Ok(UserData {
        id: fetched_data.id,
        name: processed_data.name,
        age: processed_data.age,
        processed: true,
    })
}

// 使用 map 和 map_err 转换错误
fn process_with_transformation(user_data: UserData) -> Result<UserData, AppError> {
    process_user_data(user_data)
        .map(|data| UserData {
            name: data.name.to_uppercase(),
            ..data
        })
        .map_err(|e| match e {
            AppError::ValidationError(msg) => AppError::ValidationError(format!("处理失败: {}", msg)),
            _ => e,
        })
}

// 使用 and_then 进行链式操作
fn process_with_chain(user_data: UserData) -> Result<UserData, AppError> {
    process_user_data(user_data)
        .and_then(|data| {
            if data.name.len() > 10 {
                Ok(data)
            } else {
                Err(AppError::ValidationError("用户名太短".to_string()))
            }
        })
        .map(|data| UserData {
            name: format!("处理后的_{}", data.name),
            ..data
        })
}

#[derive(Debug, Clone)]
struct UserData {
    id: u32,
    name: String,
    age: i32,
    processed: bool,
}

#[tokio::main]
async fn main() {
    let user_data = UserData {
        id: 0,
        name: "Alice".to_string(),
        age: 25,
        processed: false,
    };
    
    // 处理错误
    match handle_user(1, user_data.clone()).await {
        Ok(result) => println!("成功: {:?}", result),
        Err(error) => println!("失败: {}", error),
    }
    
    // 使用转换
    match process_with_transformation(user_data.clone()) {
        Ok(result) => println!("转换成功: {:?}", result),
        Err(error) => println!("转换失败: {}", error),
    }
    
    // 使用链式操作
    match process_with_chain(user_data) {
        Ok(result) => println!("链式处理成功: {:?}", result),
        Err(error) => println!("链式处理失败: {}", error),
    }
}
```
</UniversalEditor>

### 高级错误处理库

在实际项目中，为了更方便地定义和处理错误，Rust 社区提供了许多优秀的错误处理库，例如：

*   **`thiserror`**: 用于简化自定义错误类型的定义，特别是当错误需要包含额外信息或从其他错误类型派生时。它通过宏自动实现 `Display` 和 `Error` trait。
*   **`anyhow`**: 用于简化错误传播，特别是在应用层代码中。它提供了一个简单的 `Result` 类型别名 `anyhow::Result<T>`，并允许你轻松地将各种错误类型转换为 `anyhow::Error`。

这些库可以大大减少错误处理的样板代码，提高代码的可读性和可维护性。

---

## 🎯 练习题

### 练习 1: 实现 Option 方法

实现一个函数，从字符串中提取数字，返回 `Option<i32>`：

<details>
<summary>查看答案</summary>

```rust
fn extract_number(s: &str) -> Option<i32> {
    s.chars()
        .filter(|c| c.is_digit(10))
        .collect::<String>()
        .parse::<i32>()
        .ok()
}

fn main() {
    println!("{:?}", extract_number("abc123def")); // Some(123)
    println!("{:?}", extract_number("no numbers")); // None
    println!("{:?}", extract_number("456")); // Some(456)
}
```

</details>

### 练习 2: 自定义错误类型

创建一个自定义错误类型，用于处理文件操作：

<details>
<summary>查看答案</summary>

```rust
use std::error::Error;
use std::fmt;

#[derive(Debug)]
enum FileError {
    NotFound(String),
    PermissionDenied(String),
    InvalidFormat(String),
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::NotFound(path) => write!(f, "文件未找到: {}", path),
            FileError::PermissionDenied(path) => write!(f, "权限被拒绝: {}", path),
            FileError::InvalidFormat(msg) => write!(f, "格式无效: {}", msg),
        }
    }
}

impl Error for FileError {}

fn read_file(path: &str) -> Result<String, FileError> {
    if path.is_empty() {
        return Err(FileError::NotFound("路径为空".to_string()));
    }
    
    if path.contains("private") {
        return Err(FileError::PermissionDenied(path.to_string()));
    }
    
    if path.contains("invalid") {
        return Err(FileError::InvalidFormat("文件格式无效".to_string()));
    }
    
    Ok(format!("文件内容: {}", path))
}

fn main() {
    match read_file("test.txt") {
        Ok(content) => println!("成功: {}", content),
        Err(error) => println!("错误: {}", error),
    }
    
    match read_file("") {
        Ok(content) => println!("成功: {}", content),
        Err(error) => println!("错误: {}", error),
    }
}
```

</details>

### 练习 3: 错误传播

创建一个函数链，每个函数都可能失败，使用 `?` 操作符传播错误：

<details>
<summary>查看答案</summary>

```rust
fn validate_input(input: &str) -> Result<String, String> {
    if input.is_empty() {
        return Err("输入不能为空".to_string());
    }
    Ok(input.to_string())
}

fn process_data(data: &str) -> Result<String, String> {
    if data.len() < 3 {
        return Err("数据太短".to_string());
    }
    Ok(data.to_uppercase())
}

fn save_data(data: &str) -> Result<(), String> {
    if data.contains("error") {
        return Err("保存失败".to_string());
    }
    println!("保存数据: {}", data);
    Ok(())
}

fn process_pipeline(input: &str) -> Result<(), String> {
    let validated = validate_input(input)?;
    let processed = process_data(&validated)?;
    save_data(&processed)?;
    Ok(())
}

fn main() {
    match process_pipeline("hello") {
        Ok(_) => println!("处理成功"),
        Err(error) => println!("处理失败: {}", error),
    }
    
    match process_pipeline("") {
        Ok(_) => println!("处理成功"),
        Err(error) => println!("处理失败: {}", error),
    }
}
```

</details>

---

## 📝 总结

在这一章中，我们学习了 Rust 的错误处理机制：

1. **Option 类型**: 处理可能为空的值
2. **Result 类型**: 处理可能失败的操作
3. **错误传播**: 使用 `?` 操作符传播错误
4. **自定义错误**: 创建自己的错误类型
5. **与 JavaScript 对比**: 类型安全 vs 异常处理

### 关键要点

- Rust 使用类型系统而不是异常处理错误
- `Option` 和 `Result` 强制显式处理错误情况
- `?` 操作符简化错误传播
- 自定义错误类型提供更好的错误信息

### 下一步学习

在下一章中，我们将学习 Rust 的 Web 开发，了解如何使用 Rust 构建 Web 应用程序。

---

**继续学习**: [Web 开发实战](./module-07-web-development) 